<template>
    <div
        v-loading="isView"
        class="flow-containers"
        :class="{ 'view-mode': isView }"
    >
        <el-container style="height: 100%">
            <el-header
                style="
                    border-bottom: 1px solid rgb(218 218 218);
                    height: auto;
                    padding-left: 0px;
                "
                v-if="state !== 'readonly'"
            >
                <div
                    style="
                        display: flex;
                        padding: 10px 0px;
                        justify-content: space-between;
                    "
                >
                    <div>
                        <el-upload
                            action=""
                            :before-upload="openBpmn"
                            style="margin-right: 10px; display: inline-block"
                        >
                            <el-tooltip
                                effect="dark"
                                content="加载xml"
                                placement="bottom"
                            >
                                <el-button
                                    size="mini"
                                    icon="el-icon-folder-opened"
                                />
                            </el-tooltip>
                        </el-upload>
                        <el-tooltip
                            effect="dark"
                            content="新建"
                            placement="bottom"
                        >
                            <el-button
                                size="mini"
                                icon="el-icon-circle-plus"
                                @click="newDiagram"
                            />
                        </el-tooltip>
                        <el-tooltip
                            effect="dark"
                            content="自适应屏幕"
                            placement="bottom"
                        >
                            <el-button
                                size="mini"
                                icon="el-icon-rank"
                                @click="fitViewport"
                            />
                        </el-tooltip>
                        <el-tooltip
                            effect="dark"
                            content="放大"
                            placement="bottom"
                        >
                            <el-button
                                size="mini"
                                icon="el-icon-zoom-in"
                                @click="zoomViewport(true)"
                            />
                        </el-tooltip>
                        <el-tooltip
                            effect="dark"
                            content="缩小"
                            placement="bottom"
                        >
                            <el-button
                                size="mini"
                                icon="el-icon-zoom-out"
                                @click="zoomViewport(false)"
                            />
                        </el-tooltip>
                        <el-tooltip
                            effect="dark"
                            content="后退"
                            placement="bottom"
                        >
                            <el-button
                                size="mini"
                                icon="el-icon-back"
                                @click="modeler.get('commandStack').undo()"
                            />
                        </el-tooltip>
                        <el-tooltip
                            effect="dark"
                            content="前进"
                            placement="bottom"
                        >
                            <el-button
                                size="mini"
                                icon="el-icon-right"
                                @click="modeler.get('commandStack').redo()"
                            />
                        </el-tooltip>
                    </div>
                    <div v-if="state !== 'readonly'">
                        <!--            <el-button size="mini" icon="el-icon-s-check" @click="verifyXML">校验xml</el-button>-->
                        <el-button
                            size="mini"
                            icon="el-icon-view"
                            @click="showXML"
                            >查看xml</el-button
                        >
                        <el-button
                            size="mini"
                            icon="el-icon-download"
                            @click="saveXML(true)"
                            >下载xml</el-button
                        >
                        <el-button
                            size="mini"
                            icon="el-icon-picture"
                            @click="saveImg('svg', true)"
                            >下载svg</el-button
                        >
                        <el-button size="mini" type="primary" @click="save"
                            >保存模型</el-button
                        >
                    </div>
                </div>
            </el-header>
            <el-container style="align-items: stretch">
                <el-main style="padding: 0">
                    <div ref="canvas" class="canvas" />
                </el-main>
                <el-aside
                    style="
                        width: 400px;
                        min-height: 650px;
                        background-color: #ffffff;
                    "
                >
                    <panel
                        v-if="modeler"
                        :modeler="modeler"
                        :config="config"
                        :users="users"
                        :groups="groups"
                        :exps="exps"
                        :categorys="categorys"
                        :state="state"
                    />
                </el-aside>
            </el-container>
        </el-container>
    </div>
</template>

<script>
import { Base64 } from "js-base64";
// 汉化
import customTranslate from "./common/customTranslate";
import lintModule from "bpmn-js-bpmnlint";
import Modeler from "bpmn-js/lib/Modeler";
// import bpmnlintConfig from './.bpmnlintrc';
import panel from "./PropertyPanel";
import getInitStr from "./flowable/init";
// 引入flowable的节点文件
import FlowableModule from "./flowable/flowable.json";
import customControlsModule from "./customPanel";

export default {
    name: "WorkflowBpmnModeler",
    components: {
        panel,
    },
    props: {
        xml: {
            type: String,
            default: "",
        },
        users: {
            type: Array,
            default: () => [],
        },
        groups: {
            type: Array,
            default: () => [],
        },
        categorys: {
            type: Array,
            default: () => [],
        },
        exps: {
            type: Array,
            default: () => [],
        },
        isView: {
            type: Boolean,
            default: false,
        },
        config: {
            type: Array,
            default: () => [],
        },
        state: {
            required: false,
        },
    },
    data() {
        return {
            modeler: null,
            zoom: 1,
        };
    },
    watch: {
        xml: function (val) {
            if (val) {
                this.createNewDiagram(val);
            }
        },
    },
    mounted() {
        let options = {
            translate: ["value", customTranslate], //汉化
        };
        // this.state == "readonly"

        if (this.state == "readonly") {
            options = {
                translate: ["value", customTranslate], //汉化
                paletteProvider: ["value", ""], //禁用/清空左侧工具栏
                labelEditingProvider: ["value", ""], //禁用节点编辑
                contextPadProvider: ["value", ""], //禁用图形菜单
                bendpoints: ["value", {}], //禁用连线拖动
                zoomScroll: ["value", ""], //禁用滚动
                moveCanvas: ["value", ""], //禁用拖动整个流程图
                move: ["value", ""], //禁用单个图形拖动
            };
            this.$nextTick(() => {
                try {
                    let element = document.getElementsByClassName(
                        "djs-palette-entries"
                    );
                    let e = Array.from(element);
                    e.map((item) => {
                        item.style.display = "none";
                    });
                } catch {}
            });
        }
        // 生成实例
        this.modeler = new Modeler({
            container: this.$refs.canvas,
            additionalModules: [
                lintModule,
                customControlsModule,
                {
                    ...options,
                },
            ],
            // 去除流程校验器,有需求可自行添加,需要在package.json 加入 "bpmnlint-plugin-local": "file:bpmnlint-plugin-local"
            // linting: {
            //   bpmnlint: bpmnlintConfig
            // },
            moddleExtensions: {
                flowable: FlowableModule,
            },
        });
        // 新增流程定义
        if (!this.xml) {
            this.newDiagram();
        } else {
            this.createNewDiagram(this.xml);
        }
    },
    methods: {
        newDiagram() {
            this.createNewDiagram(getInitStr());
        },
        // 让图能自适应屏幕
        fitViewport() {
            this.zoom = this.modeler.get("canvas").zoom("fit-viewport");
            const bbox = document
                .querySelector(".flow-containers .viewport")
                .getBBox();
            const currentViewbox = this.modeler.get("canvas").viewbox();
            const elementMid = {
                x: bbox.x + bbox.width / 2 - 65,
                y: bbox.y + bbox.height / 2,
            };
            this.modeler.get("canvas").viewbox({
                x: elementMid.x - currentViewbox.width / 2,
                y: elementMid.y - currentViewbox.height / 2,
                width: currentViewbox.width,
                height: currentViewbox.height,
            });
            this.zoom = (bbox.width / currentViewbox.width) * 1.8;
        },
        // 放大缩小
        zoomViewport(zoomIn = true) {
            this.zoom = this.modeler.get("canvas").zoom();
            this.zoom += zoomIn ? 0.1 : -0.1;
            this.modeler.get("canvas").zoom(this.zoom);
        },
        async createNewDiagram(data) {
            // 将字符串转换成图显示出来
            // data = data.replace(/<!\[CDATA\[(.+?)]]>/g, '&lt;![CDATA[$1]]&gt;')
            data = data.replace(/<!\[CDATA\[(.+?)]]>/g, function (match, str) {
                return str.replace(/</g, "&lt;");
            });
            try {
                await this.modeler.importXML(data);
                // this.adjustPalette()
                this.fitViewport();
            } catch (err) {
                console.error(err.message, err.warnings);
            }
        },
        // 对外 api
        getProcess() {
            const element = this.getProcessElement();
            return {
                id: element.id,
                name: element.name,
                category: element.$attrs["flowable:processCategory"],
            };
        },
        getProcessElement() {
            const rootElements = this.modeler.getDefinitions().rootElements;
            for (let i = 0; i < rootElements.length; i++) {
                if (rootElements[i].$type === "bpmn:Process")
                    return rootElements[i];
            }
        },
        async verifyXML() {
            const linting = this.modeler.get("linting");
            linting.toggle();
        },
        async saveXML(download = false) {
            try {
                const { xml } = await this.modeler.saveXML({ format: true });
                if (download) {
                    this.downloadFile(
                        `${this.getProcessElement().name}.bpmn20.xml`,
                        xml,
                        "application/xml"
                    );
                }
                return xml;
            } catch (err) {
                console.log(err);
            }
        },
        async showXML() {
            try {
                const xml = await this.saveXML();
                this.$emit("showXML", xml);
            } catch (err) {
                console.log(err);
            }
        },
        async saveImg(type = "svg", download = false) {
            try {
                const { svg } = await this.modeler.saveSVG({ format: true });
                if (download) {
                    this.downloadFile(
                        this.getProcessElement().name,
                        svg,
                        "image/svg+xml"
                    );
                }
                return svg;
            } catch (err) {
                console.log(err);
            }
        },
        async save() {
            let rejectNode = [];
            // let model = this.modeler.get("elementRegistry")._elements
            let model = this.modeler.getDefinitions();
            let model1 = model.rootElements[0];
            let model2 = model1.flowElements;
            model2.map((res) => {
                let copyUserIds,
                    candidateGroups,
                    candidateUsers,
                    refuseUserIds,
                    rejectActivity,
                    activityPermission,
                    auditConfig,
                    adjuctIds;

                if (
                    res?.$attrs?.["flowable:copyUserIds"] &&
                    typeof res?.$attrs?.["flowable:copyUserIds"] == "string"
                ) {
                    copyUserIds =
                        res.$attrs["flowable:copyUserIds"]?.split(",");
                }
                if (res?.candidateGroups) {
                    if (typeof res?.candidateGroups == "string") {
                        candidateGroups = res?.candidateGroups?.split(",");
                    } else {
                        candidateGroups = res.candidateGroups ?? [];
                    }
                }
                if (
                    res?.$attrs?.["flowable:candidateGroups"] &&
                    typeof res?.$attrs?.["flowable:candidateGroups"] == "string"
                ) {
                    candidateGroups =
                        res.$attrs["flowable:candidateGroups"]?.split(",");
                }
                if (
                    res?.$attrs?.["flowable:candidateUsers"] &&
                    typeof res?.$attrs?.["flowable:candidateUsers"] == "string"
                ) {
                    candidateUsers =
                        res.$attrs["flowable:candidateUsers"]?.split(",");
                }
                if (
                    res?.$attrs?.["flowable:refuseUserIds"] &&
                    typeof res?.$attrs?.["flowable:refuseUserIds"] == "string"
                ) {
                    refuseUserIds =
                        res.$attrs["flowable:refuseUserIds"]?.split(",");
                }
                if (
                    res?.$attrs?.["flowable:adjuctIds"] &&
                    typeof res?.$attrs?.["flowable:adjuctIds"] == "string"
                ) {
                    adjuctIds = res.$attrs["flowable:adjuctIds"]?.split(",");
                }
                if (
                    res?.$attrs?.["flowable:rejectActivity"] &&
                    typeof res?.$attrs?.["flowable:rejectActivity"] == "string"
                ) {
                    rejectActivity =
                        res.$attrs["flowable:rejectActivity"]?.split(",");
                }
                if (res?.$attrs?.["flowable:executionListener"]) {
                    activityPermission = JSON.stringify(
                        Base64.decode(res.$attrs["flowable:executionListener"])
                    );
                }
                if (
                    res?.$attrs?.["flowable:auditConfig"] &&
                    typeof res?.$attrs?.["flowable:auditConfig"] == "string"
                ) {
                    auditConfig =
                        res.$attrs["flowable:auditConfig"]?.split(",");
                }

                //
                if (
                    res.$type == "bpmn:Task" &&
                    Object.keys(res.$attrs).length > 0
                ) {
                    let obj = {
                        activityId: res.$attrs["flowable:id"], // id
                        activityName: res.$attrs["flowable:name"], // 名称
                        formId: res.$attrs["flowable:formId"], // formId
                        pageAddress: res.$attrs["flowable:pageAddress"], // 关联页面地址
                        // customForm: res.$attrs["flowable:customForm"],
                        activityPermission:
                            activityPermission ||
                            res.$attrs["flowable:executionListener"] ||
                            [], // 节点权限
                        auditConfig:
                            auditConfig ||
                            res.$attrs["flowable:auditConfig"] ||
                            [], // 审批设置
                        rejectActivity:
                            rejectActivity ||
                            res.$attrs["flowable:rejectActivity"] ||
                            [], // 驳回节点
                        isCopy: res.$attrs["flowable:isCopy"], // 是否抄送
                        flowType: res.$attrs["flowable:flowType"],
                        isAvgUser: isNaN(res.$attrs["flowable:isAvgUser"] * 1)
                            ? 0
                            : res.$attrs["flowable:isAvgUser"] * 1, // 是否抄送
                        copyUserIds:
                            copyUserIds ||
                            res?.$attrs?.["flowable:copyUserIds"] ||
                            [], // 抄送人员
                        candidateGroups:
                            candidateGroups ||
                            res.$attrs["flowable:candidateGroups"] ||
                            [], // 审批角色
                        isPortUser: res.$attrs["flowable:isPortUser"], // 是否指定审批人员
                        candidateUsers:
                            candidateUsers ||
                            res.$attrs["flowable:candidateUsers"] ||
                            [], // 审批用户
                        refuseUserIds:
                            refuseUserIds ||
                            res.$attrs["flowable:refuseUserIds"] ||
                            [], // 可拒绝用户
                        adjuctIds:
                            adjuctIds || res.$attrs["flowable:adjuctIds"] || [], // 调单用户
                    };
                    rejectNode.push(obj);
                } else if (
                    res.$type == "bpmn:UserTask" &&
                    Object.keys(res.$attrs).length > 0
                ) {
                    let obj = {
                        activityId: res.$attrs["flowable:id"], // id
                        activityName: res.$attrs["flowable:name"], // 名称
                        formId: res.$attrs["flowable:formId"], // formId
                        pageAddress: res.$attrs["flowable:pageAddress"], // 关联页面地址
                        // customForm: res.$attrs["flowable:customForm"],
                        activityPermission:
                            activityPermission ||
                            res.$attrs["flowable:executionListener"] ||
                            [], // 节点权限
                        auditConfig:
                            auditConfig ||
                            res.$attrs["flowable:auditConfig"] ||
                            [], // 审批设置
                        rejectActivity:
                            rejectActivity ||
                            res.$attrs["flowable:rejectActivity"] ||
                            [], // 驳回节点
                        isCopy: res.$attrs["flowable:isCopy"], // 是否抄送
                        flowType: res.$attrs["flowable:flowType"],
                        isAvgUser: isNaN(res.$attrs["flowable:isAvgUser"] * 1)
                            ? 0
                            : res.$attrs["flowable:isAvgUser"] * 1, // 是否抄送
                        copyUserIds:
                            copyUserIds ||
                            res?.$attrs?.["flowable:copyUserIds"] ||
                            [], // 抄送人员
                        candidateGroups:
                            candidateGroups ||
                            res.$attrs["flowable:candidateGroups"] ||
                            [], // 审批角色
                        isPortUser: res.$attrs["flowable:isPortUser"], // 是否指定审批人员
                        candidateUsers:
                            candidateUsers ||
                            res.$attrs["flowable:candidateUsers"] ||
                            [], // 审批用户
                        refuseUserIds:
                            refuseUserIds ||
                            res.$attrs["flowable:refuseUserIds"] ||
                            [], // 可拒绝用户
                        adjuctIds:
                            adjuctIds || res.$attrs["flowable:adjuctIds"] || [], // 调单用户
                    };
                    rejectNode.push(obj);
                }
                //  else if (
                //     res.$type == "bpmn:SequenceFlow" &&
                //     Object.keys(res.$attrs).length > 0
                // ) {
                //     let obj = {
                //         activityId: res.$attrs["flowable:id"], // id
                //         value: res.$attrs["flowable:value"], // id
                //     };
                //     rejectNode.push(obj);
                // }
            });
            const process = this.getProcess();
            const xml = await this.saveXML();
            const svg = await this.saveImg();
            const result = { process, xml, svg, rejectNode };
            // console.log(rejectNode);
            // return;
            this.$emit("save", result);
            window.parent.postMessage(result, "*");
        },
        openBpmn(file) {
            const reader = new FileReader();
            reader.readAsText(file, "utf-8");
            reader.onload = () => {
                this.createNewDiagram(reader.result);
            };
            return false;
        },
        downloadFile(filename, data, type) {
            const a = document.createElement("a");
            const url = window.URL.createObjectURL(
                new Blob([data], { type: type })
            );
            a.href = url;
            a.download = filename;
            a.click();
            window.URL.revokeObjectURL(url);
        },
    },
};
</script>

<style lang="scss" scoped>
/*左边工具栏以及编辑节点的样式*/
@import "~bpmn-js/dist/assets/diagram-js.css";
@import "~bpmn-js/dist/assets/bpmn-font/css/bpmn.css";
@import "~bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css";
@import "~bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css";

//@import "~bpmn-js-bpmnlint/dist/assets/css/bpmn-js-bpmnlint.css";
.view-mode {
    .el-header,
    .el-aside,
    .djs-palette,
    .bjs-powered-by {
        display: none;
    }

    .el-loading-mask {
        background-color: initial;
    }

    .el-loading-spinner {
        display: none;
    }
}

.flow-containers {
    // background-color: #ffffff;
    width: 100%;
    height: 100%;

    .canvas {
        width: 100%;
        height: 100%;
        //flex: 1;
        //position: relative;
        //background: url("")
        //repeat !important;
        //div.toggle-mode {
        //  display: none;
        //}
    }

    .panel {
        position: absolute;
        right: 0;
        top: 50px;
        width: 300px;
    }

    .load {
        margin-right: 10px;
    }

    .el-form-item__label {
        font-size: 13px;
    }

    .djs-palette {
        left: 0px !important;
        top: 0px;
        border-top: none;
    }

    .djs-container svg {
        //min-height: 650px;
    }

    .highlight.djs-shape .djs-visual > :nth-child(1) {
        fill: green !important;
        stroke: green !important;
        fill-opacity: 0.2 !important;
    }

    .highlight.djs-shape .djs-visual > :nth-child(2) {
        fill: green !important;
    }

    .highlight.djs-shape .djs-visual > path {
        fill: green !important;
        fill-opacity: 0.2 !important;
        stroke: green !important;
    }

    .highlight.djs-connection > .djs-visual > path {
        stroke: green !important;
    }

    // .djs-connection > .djs-visual > path {
    //   stroke: orange !important;
    //   stroke-dasharray: 4px !important;
    //   fill-opacity: 0.2 !important;
    // }
    // .djs-shape .djs-visual > :nth-child(1) {
    //   fill: orange !important;
    //   stroke: orange !important;
    //   stroke-dasharray: 4px !important;
    //   fill-opacity: 0.2 !important;
    // }
    .highlight-todo.djs-connection > .djs-visual > path {
        stroke: orange !important;
        stroke-dasharray: 4px !important;
        fill-opacity: 0.2 !important;
    }

    .highlight-todo.djs-shape .djs-visual > :nth-child(1) {
        fill: orange !important;
        stroke: orange !important;
        stroke-dasharray: 4px !important;
        fill-opacity: 0.2 !important;
    }

    .overlays-div {
        font-size: 10px;
        color: red;
        width: 100px;
        top: -20px !important;
    }
}
</style>
